package org.jing.ext.jdbc.mybatis;

import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.jing.ext.jdbc.JingJdbcException;

import java.util.concurrent.ConcurrentHashMap;

/**
 * Description: <br>
 *
 * @author bks <br>
 * @since 2021-09-13 <br>
 */
@SuppressWarnings("unchecked")
public abstract class BaseMyBatisDomain {
    private MyBatis myBatis;

    private ConcurrentHashMap<SqlSession, ConcurrentHashMap<Class, Object>> sessionMap = new ConcurrentHashMap<>();

    protected void setMyBatis(MyBatis myBatis) {
        this.myBatis = myBatis;
    }

    protected MyBatis getMyBatis() {
        return this.myBatis;
    }

    /**
     * Description: 根据指定的SqlSession对象获取指定的mapper实例. <br>
     * 如果指定的SqlSession不在缓存sessionMap中, 则会给指定的SqlSession创建一个新的map用来存储mapper实例. <br>
     *
     * @param session SqlSession <br>
     * @param mapperClass mapper <br>
     * @param <T> <br>
     * @return <br>
     */
    protected <T> T getMapper(SqlSession session, Class<T> mapperClass) {
        ConcurrentHashMap<Class, Object> mapperMap = sessionMap.computeIfAbsent(session, k -> new ConcurrentHashMap<>());
        if (mapperMap.containsKey(mapperClass)) {
            return (T) mapperMap.get(mapperClass);
        }
        else {
            T instance = session.getMapper(mapperClass);
            mapperMap.put(mapperClass, instance);
            return instance;
        }
    }

    /**
     * Description: 根据当前的SqlSession对象获取指定的mapper实例. <br>
     * 如果当前的SqlSession不在缓存sessionMap中, 则会给指定的SqlSession创建一个新的map用来存储mapper实例. <br>
     *
     * @param mapperClass mapper <br>
     * @param <T> <br>
     * @return <br>
     */
    protected <T> T getMapperByCurrentSession(Class<T> mapperClass) throws JingJdbcException {
        return getMapper(getMyBatis().session(), mapperClass);
    }

    /**
     * Description: 根据指定的参数创建一个新的SqlSession对象, 然后获取指定的mapper实例. <br>
     *
     * @param autoCommit 自动提交 <br>
     * @param type 执行类型 <br>
     * @param mapperClass mapper <br>
     * @param <T> <br>
     * @return <br>
     */
    protected <T> T getMapperByNewSession(boolean autoCommit, ExecutorType type, Class<T> mapperClass) throws JingJdbcException {
        SqlSession session = getMyBatis().newSession(type, autoCommit);
        return getMapper(session, mapperClass);
    }

    /**
     * Description: 根据指定的参数创建一个新的SqlSession对象, 然后获取指定的mapper实例. <br>
     *
     * @param type 执行类型 <br>
     * @param mapperClass mapper <br>
     * @param <T> <br>
     * @return <br>
     */
    protected <T> T getMapperByNewSession(ExecutorType type, Class<T> mapperClass) throws JingJdbcException {
        SqlSession session = getMyBatis().newSession(type);
        return getMapper(session, mapperClass);
    }

    /**
     * Description: 根据指定的参数创建一个新的SqlSession对象, 然后获取指定的mapper实例. <br>
     *
     * @param autoCommit 自动提交 <br>
     * @param mapperClass mapper <br>
     * @param <T> <br>
     * @return <br>
     */
    protected <T> T getMapperByNewSession(boolean autoCommit, Class<T> mapperClass) throws JingJdbcException {
        SqlSession session = getMyBatis().newSession(autoCommit);
        return getMapper(session, mapperClass);
    }

    /**
     * Description: 释放顶层session以及存储其所有mapper实例的map. <br>
     * 注意, 此方法一般在需要新建事务做更新操作后马上提交的场景使用,
     * 期望是和getMapperByNewSession方法联动使用, 不然会引起不必要的麻烦. <br>
     */
    protected void releaseTopSession() throws JingJdbcException {
        SqlSession topSession = getMyBatis().session();
        getMyBatis().release(topSession);
        sessionMap.remove(topSession);
    }
}
